#include "Display.h"

#include "Utility.h"

namespace 
{
	using Utility::Error::CheckDXError;

	com_ptr<IDXGISwapChain> CreateD3DSwapChain(ID3D11Device *device, DXGI_SWAP_CHAIN_DESC* desc)
	{
		com_ptr<IDXGIDevice1> dxgidevice;
        void*  dxgidevice_raw;
		HRESULT result = device->QueryInterface(IID_IDXGIDevice1, &dxgidevice_raw);
		dxgidevice = make_com_ptr(reinterpret_cast<IDXGIDevice1*>(dxgidevice_raw));
		CheckDXError(result, "IDXGIDevice1::QueryInterface");

		com_ptr<IDXGIAdapter1> dxgiadapter;
		void* dxgiadapter_raw = nullptr;
		result = dxgidevice->GetParent(IID_IDXGIAdapter1, &dxgiadapter_raw); 
		dxgiadapter = make_com_ptr(reinterpret_cast<IDXGIAdapter1*>(dxgiadapter_raw));
		CheckDXError(result, "IDXGIDevice1::GetParent()");

		com_ptr<IDXGIFactory1> dxgifactory;
		void* dxgifactory_raw = nullptr;
		result = dxgiadapter->GetParent(IID_IDXGIFactory1, &dxgifactory_raw);
		dxgifactory = make_com_ptr(reinterpret_cast<IDXGIFactory1*>(dxgifactory_raw));
		CheckDXError(result, "IDXGIAdapter1::QueryInterface()");

		com_ptr<IDXGISwapChain> swapchain;
		IDXGISwapChain* swapchain_raw = nullptr;
		result = dxgifactory->CreateSwapChain(device, desc, &swapchain_raw);
		swapchain = make_com_ptr(swapchain_raw);
		CheckDXError(result, "IDXGIFactory1::CreateSwapChain()");
		
		return swapchain;
	}

	com_ptr<ID3D11RenderTargetView> CreateRenderTargetViewFromSwapChain(ID3D11Device* device, IDXGISwapChain* swapchain)
	{
		ID3D11RenderTargetView* rtv = nullptr;
		ID3D11Resource* res = nullptr;
		HRESULT result = swapchain->GetBuffer (0, __uuidof(ID3D11Texture2D), (LPVOID*)&res);
		com_ptr<ID3D11Resource> resource = make_com_ptr (res);

		result = device->CreateRenderTargetView (resource, nullptr, &rtv);

		CheckDXError(result, "ID3D11Device::CreateRenderTargetView");
	
		return make_com_ptr(rtv);
	}
}

Display::Display(ID3D11Device* device, HWND hwnd, unsigned int width, unsigned int height, bool windowed)
	: m_Device(device)
{
	DXGI_SWAP_CHAIN_DESC desc;
	desc.BufferDesc.Width = width;
	desc.BufferDesc.Height = height;
	desc.BufferDesc.RefreshRate.Numerator = 0;
	desc.BufferDesc.RefreshRate.Denominator = 1;
	desc.BufferDesc.Format = DisplayFormat;
	desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
	desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
	desc.SampleDesc.Count = 1;
	desc.SampleDesc.Quality = 0;
	desc.BufferUsage = DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_RENDER_TARGET_OUTPUT;
	desc.BufferCount = 2;
	desc.OutputWindow = hwnd;
	desc.Windowed = windowed;
	desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
	desc.Flags = DisplayFlags;

	m_SwapChain = CreateD3DSwapChain(device, &desc);
	m_RenderTargetView = CreateRenderTargetViewFromSwapChain(device, m_SwapChain);
}

void Display::Resize(unsigned int width, unsigned int height)
{
	m_RenderTargetView->Release();
	m_SwapChain->ResizeBuffers(1, width, height, DisplayFormat, DisplayFlags);
	m_RenderTargetView = CreateRenderTargetViewFromSwapChain(m_Device, m_SwapChain);
}

void Display::ClearScreen(FLOAT color[])
{
	ID3D11DeviceContext* context_raw;
	m_Device->GetImmediateContext(&context_raw);
	com_ptr<ID3D11DeviceContext> context = make_com_ptr(context_raw);

	context->ClearRenderTargetView (GetRenderTargetView(), color);
}

void Display::Present()
{
	m_SwapChain->Present(0, 0);
}

com_ptr<ID3D11RenderTargetView> Display::GetRenderTargetView() const
{
	return m_RenderTargetView;
}